; *****************************************************************
; ISO9660 - level 1 / simple driver
; Part of kernel loader (RDKRLD.SYS) - Second Stage Boot Loader
;
; Copyrights 2011 Michal Saman, m.saman@designplus.cz.
; This source code is release under the Apache License 2.0.
; http://www.apache.org/licenses/LICENSE-2.0
; *****************************************************************

; some constants
%define	ISO9660_PVD_RD_OFFSET		156				; location of ROOT DIRECTORY ENTRY inside Primary Volume Descriptor
%define	ISO9660_RD_EXT_LOC			2				; Location of extent (LBA) in both-endian format.

; data section
iso_sector_size				dw		2048
iso_ext_sectors				dw		0
iso_pri_vol_descriptor		dd		0				; Primary volume descripotro - LBA address
iso_msg_find_failure		db		"Error, can't found file: ", 0

; ISO9660 directory entry structure
struc iso_de_t
	.de_size			resb 1		; Length of Directory Record.
	.attr_size			resb 1		; Extended Attribute Record length.
	.ext_loc			resd 2		; Location of extent (LBA) in both-endian format.
	.ext_size			resd 2		; Data length (size of extent) in both-endian format.
	.datetime			resb 7		; Recording date and time (see format below).
	.flags				resb 1		; File flags (see below).
	.unit_size			resb 1		; File unit size for files recorded in interleaved mode, zero otherwise.
	.gap_size			resb 1		; Interleave gap size for files recorded in interleaved mode, zero otherwise.
	.ext_volume			resd 1		; Volume sequence number - the volume that this extent is recorded on, in 16 bit both-endian format.
	.fn_size			resb 1		; Length of file identifier (file name). This terminates with a ';' character followed by the file ID number in ASCII coded decimal ('1').
	; variable size - File identifier - name of file.
	; variable size - Padding field - zero if length of file identifier is odd, otherwise, this field is not present. This means that a directory entry will always start on an even byte number.
endstruc

iso_de:
istruc iso_de_t
	at iso_de_t.de_size,		db 0
	at iso_de_t.attr_size,		db 0
	at iso_de_t.ext_loc,		dd 0, 0
	at iso_de_t.ext_size,		dd 0, 0
	at iso_de_t.datetime,		db 0, 0, 0, 0, 0, 0, 0
	at iso_de_t.flags, 			db 0
	at iso_de_t.unit_size, 		db 0
	at iso_de_t.gap_size,		db 0
	at iso_de_t.ext_volume,		dd 0
	at iso_de_t.fn_size,		db 1
iend

; ************************************************************
; Find file on ISO9660 filesystem and loads it to read buffer
; -----------------------------------------------------------
;
; Input:
; [boot_drive] - number of drive
; [iso_pri_vol_descriptor] - LBA address of Primary vol. desc.
; imageName_iso - memory location thaht point to file name
;
; Returns:
; AX = status code, 0 - all OK, 1 - error
; CX = size of file in sectors
;
; Note:
; 1) Function change all registers and flags. Save it before
;    you call this function, if you need them later.
; 2) Function don't search recursivly into directories.
;    Loaded file must be in root directory.
; 3) We load extent of directory to one segment limited
;    to 64kB. Number of max. entries in directory is
;	 limited to this size!
; ************************************************************
loadFileIso:
	;pusha
	;pushf
	jmp 	.start

.load_image:
	push	ds											; save current data segment
	mov		ax, READ_BUFF_SEG
	mov		ds, ax										; change DS to read buffer segment
	xor		ax, ax
	xor		dx, dx
	mov		ax, WORD [ds:bx + iso_de_t.ext_size]					; save size of extent (in bytes) to AX
	mov		bx, WORD [ds:bx + iso_de_t.ext_loc]						; save LBA of sector (that contains image of 2nd loader) extent to BX
	pop		ds

	xor 	dx, dx
	div 	WORD [iso_sector_size]
	cmp		dx, 0
	je		.increase_skip2
	add		ax, 1									; zvys sektror o 1, pokud mel vysledek deleni nejaky zbytek
	.increase_skip2:

	mov		cx, ax										; count of sectors to read to CX
	mov		dl, [boot_drive]							; disk number
	;mov		BYTE [re_debug_mode], 1

	call	read_sector_ext

	mov		si, msgCrLf
	call 	Puts16

	mov		ax, 0								; set return status OK
	jmp		.exit

.start:
	xor		dx, dx
	mov		dl, [boot_drive]					; load drive number to read from
	mov		bx, [iso_pri_vol_descriptor]		; LBA address of primary volume descriptor (PVD)
	mov		cx, 1								; read one sector
	;mov		BYTE [re_debug_mode], 1
	call	read_sector_ext						; read PVD to READ_BUFF_SEG:READ_BUFF_OFF
	; get position and size of extent (AX, BX)
	mov		ax, READ_BUFF_SEG							; set seg. to read buffer segment
	mov		bx, READ_BUFF_OFF + ISO9660_PVD_RD_OFFSET	; set off. to read buffer offset
	push	ds											; save current data segment
	mov		ds, ax										; change DS to read buffer segment
	xor		ax, ax
	mov		ax, WORD [ds:bx + iso_de_t.ext_size]		; save size of extent (in bytes) to AX
	mov		bx, WORD [ds:bx + iso_de_t.ext_loc]			; save sector that contains root directory extent to BX
	pop		ds											; return do our default data segment
	push	ax											; save actual size of extent to memory
	; count of sectors to read (CX)
	xor		dx, dx
	div		WORD [iso_sector_size]						; divide size of extent by size of sector
	cmp		dx, 0
	je		.increase_skip
	add		ax, 1										; add 1 to sector count
	.increase_skip:
	;call	PrintRegister16
	mov		cx, ax										; save count of sectors to CX
	mov		WORD [iso_ext_sectors], ax					; save count of sectors to memory

	xor		dx, dx
	mov		dl, BYTE [boot_drive]						; load drive number to read from
	call	read_sector_ext								; read root directory extent to READ_BUFF_SEG:READ_BUFF_OFF
	pop 	cx											; restore size of extent to CX

	xor		dx, dx										; clear DX - we use as pointer to actuall DirEntry inside extent
	mov		bx, READ_BUFF_OFF							; read buffer offset set to bx (it point start of extent)
.dir_list:
	push	ds										; uloz vychozi ds
	mov		ax, READ_BUFF_SEG						; segment nastav na adresu read buffer
	mov		ds, ax									; nastav ds na sector read buffer segment
	xor		ax, ax
	mov		al, BYTE [ds:bx + iso_de_t.de_size]		; do ax si uloz velikost aktualni DirEntry
	;call	PrintRegister16							; print contents of ax register
	cmp		al, 0									; je velikost DirEntry == 0?
	je		.check_next_sector						; pokud se rovna nule, mozna jsme mozna na konci extentu, musime ale proveri jestli extent nepokracuje dalsim sektorem

	push	ax
	push	cx										; uloz cx (celovy pocet sektoru extentu)
	push	bx

	xor		cx, cx									; cx = 0
	mov		cl, BYTE [ds:bx + iso_de_t.fn_size]		; cl = velikost souboru
	;sub		cl, 2								; sniz velikost souboru o 2 znaky, orizne ";1" na konci kazdeho souboru
	add		bx, iso_de_t.fn_size + 1				; zvys adresu bx na pozici jmena souboru
	mov		si, bx									; nastav SI na pozici jmena souboru
	mov		di, imageName_iso						; nastav pozici di na nazev souboru, ktery hledame
	;mov		cx, 12
	rep	cmpsb										; porovnej DS:SI == ES:DI

	pop		bx
	pop		cx										; obnov cx (celovy pocet sektoru extentu)
	pop		ax

	pop		ds										; obnov ds

	je		.load_image

	add		dx, ax									; zvys hodnotu dx o velikost DirEntry
	add		bx, ax									; zvys hodnotu bx o velikost DirEntry

	;mov	si, msgCrLf
	;call	Puts16
	cmp		dx, WORD [iso_sector_size]					; je dx mensi nez velikos jednoho sektoru ??
	jb		.dir_list								; pokud ano, smycka

.check_next_sector:
	pop		ds										; restore to our default DS
	;mov	si, msgOk
	;call	Puts16
	sub		bx, READ_BUFF_OFF						; sniz bx (ukazatel na aktualni DirEntry) o read buffer offset
	mov		ax, bx									; uloz ukazatel na aktualni DirEntry do AX
	;call	PrintRegister16
	xor		dx, dx									; set DX = 0
	div		WORD [iso_sector_size]						; vydel AX velikosti sektoru, tim ziskame poradove cislo v jakem sektoru se nachazime
	cmp		dx, 0
	je		.increase_skip1
	add		ax, 1									; zvys sektror o 1, pokud mel vysledek deleni nejaky zbytek
	.increase_skip1:
	;mov	si, msgOk2
	;call	Puts16
	;call	PrintRegister16
	;mov	si, msgCrLf
	;call	Puts16

	cmp		ax, WORD [iso_ext_sectors]					; je ax = ext_sectors ; celovy pocet sektoru extendu = nas aktualni sektor, ktery jsme precetli
	je		.done									; ... pak konec, mame precteno vse ...
	mul		WORD [iso_sector_size]						; vynasob nase aktualni cislo sektoru extentu (AX) velikosti sektoru (2048), tim nastavime ukazatel na zatek dalsiho sektoru.
	mov		bx, ax									; uloz vysledek (pozici nasledujici dirEntry) do BX
	add		bx, READ_BUFF_OFF						; zvys pozici o read buffer offset (tam se nachazi extent)
	mov		ax, bx
	;call	PrintRegister16
	xor		dx, dx									; dx (ukazatel na aktualni DirEntry v ramci sektoru) nastav na zacatek - 0
	jmp		.dir_list								; vrat se na zacatek cteci smysky adresare (bude poracovat dalsim sektorem extentu)

.done:
	mov		ax, PRG_CODE_SEGMENT
	mov		si, msgCrLf
	call	Puts16
	mov		si, iso_msg_find_failure
	call	Puts16
	mov		si, imageName_iso
	call	Puts16
	mov		ax, 1									; set status error

.exit:
	;popa
	;popf
	ret
